﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Net;
using System.IO;
using System.Text;
using System.Collections;
using AjaxControlToolkit;

namespace HTTPDebuggerTools.com
{
    public partial class ViewstateDecoder : System.Web.UI.Page
    {
        //private string indentString = "   ";
        private TextWriter tw = new StringWriter();

        protected void Page_Load(object sender, EventArgs e)
        {
            NoBotState state;
            if (!NoBot1.IsValid(out state))
                Response.Redirect("http://www.httpdebugger.com/");

        }

        protected void DecodeBtn_Click(object sender, EventArgs e)
        {
            bool isValid = (ViewStateBox.Text.Length != 0);
            string viewstate = "";

            try
            {
                //check if ViewStateBox.Text contains an URL
                if (ViewStateBox.Text.IndexOf("http://") != -1 || ViewStateBox.Text.IndexOf("https://") != -1)
                {
                    isValid = GetPageViewState(ViewStateBox.Text, ref viewstate);
                    if (isValid)
                    {
                        ViewStateBox.Text += "\r\n\r\n" + viewstate;
                    }
                    else
                        ViewStateBox.Text = "Error occurred while retrieving the ViewState data from the provided URL:\r\n\r\n" + ViewStateBox.Text;
                }
                else
                    viewstate = ViewStateBox.Text;

                //check if we have valid viewstate
                if (isValid)
                {
                    LosFormatter los = new LosFormatter();
                    object viewState = los.Deserialize(viewstate);

                    ParseViewStateGraph(viewState, 0, string.Empty);

                    double len = 1.0 * viewstate.Length / 1024;

                    ResultLiteral.Text = "<strong>ViewState Size: " + len.ToString("N1") + " kb</strong><br><br>" + tw.ToString() + "<br>";
                    ResultLiteral.Visible = true;
                }
            }
            catch
            {
                ViewStateBox.Text = "Error occurred while decoding the ViewState data.";
            }
        }

        bool GetPageViewState(string url, ref string viewstate)
        {
            bool res = false;            

            try
            {
                string html = GetUrlContent(url);

                //now extract the _VIEWSTATE from html
                if (html.Length != 0)
                {
                    int pos = html.IndexOf("__VIEWSTATE");
                    if (pos != -1)
                    {
                        int start = pos;
                        int end = pos;

                        while (start-- >= 0)
                            if (html[start] == '<')
                                break;

                        while (end++ <= html.Length)
                            if (html[end] == '>')
                                break;

                        string tag = html.Substring(start, end - start);

                        //now find the tag's value                        
                        pos = tag.IndexOf("value");
                        if (pos != -1)
                        {
                            start = pos + 5; //length of 'value'
                            while (tag[start] == ' ' || tag[start] == '=' || tag[start] == '\"' || tag[start] == '\'')
                                start++;

                            for (end = start; end < tag.Length; end++)
                                if (tag[end] == '\"' || tag[end] == '\'')
                                    break;

                            viewstate = tag.Substring(start, end - start);
                            res = true;
                        }
                    }
                }
            }
            catch
            {

            }

            return res;
        }
        
        string GetUrlContent(string url)
        {
            string res = "";
            
            try
            {                
                // Create a request for the URL. 		
                WebRequest request = WebRequest.Create(url);
                // If required by the server, set the credentials.
                request.Credentials = CredentialCache.DefaultCredentials;
                // Get the response.
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                // Get the stream containing content returned by the server.
                Stream dataStream = response.GetResponseStream();
                // Open the stream using a StreamReader for easy access.
                StreamReader reader = new StreamReader(dataStream);
                
                // Read the content.
                res = reader.ReadToEnd();

                // Cleanup the streams and the response.
                reader.Close();
                dataStream.Close();
                response.Close();
            }
            catch
            {

            }

            return res;
        }

        /// <summary>
        /// Recursively parses the view state.
        /// </summary>
        /// <param name="node">The current view state node.</param>
        /// <param name="depth">The "depth" of the view state tree.</param>
        /// <param name="label">A label to display in the emitted output next to the current node.</param>
        void ParseViewStateGraph(object node, int depth, string label)
        {
            if (depth != 0)
                tw.Write("<br />");

            if (node == null)
            {
                tw.Write(String.Concat(Indent(depth), label, "NODE IS NULL"));
            }
            else if (node is Triplet)
            {
                tw.Write(String.Concat(Indent(depth), label, "TRIPLET"));
                ParseViewStateGraph(((Triplet)node).First, depth + 1, "First: ");
                ParseViewStateGraph(((Triplet)node).Second, depth + 1, "Second: ");
                ParseViewStateGraph(((Triplet)node).Third, depth + 1, "Third: ");
            }
            else if (node is Pair)
            {
                tw.Write(String.Concat(Indent(depth), label, "PAIR"));
                ParseViewStateGraph(((Pair)node).First, depth + 1, "First: ");
                ParseViewStateGraph(((Pair)node).Second, depth + 1, "Second: ");
            }
            else if (node is ArrayList)
            {
                tw.Write(String.Concat(Indent(depth), label, "ARRAYLIST"));

                // display array values
                for (int i = 0; i < ((ArrayList)node).Count; i++)
                    ParseViewStateGraph(((ArrayList)node)[i], depth + 1, String.Format("({0}) ", i));
            }
            else if (node.GetType().IsArray)
            {
                tw.Write(String.Concat(Indent(depth), label, "ARRAY "));
                tw.Write(String.Concat("(", node.GetType().ToString(), ")"));
                IEnumerator e = ((Array)node).GetEnumerator();
                int count = 0;
                while (e.MoveNext())
                    ParseViewStateGraph(e.Current, depth + 1, String.Format("({0}) ", count++));
            }
            else if (node.GetType().IsPrimitive || node is string)
            {
                tw.Write(String.Concat(Indent(depth), label));
                tw.Write(node.ToString() + " (" + node.GetType().ToString() + ")");
            }
            else
            {
                tw.Write(String.Concat(Indent(depth), label, "OTHER - "));
                tw.Write(node.GetType().ToString());
            }
        }

        /// <summary>
        /// Returns a string containing the <see cref="IndentString"/> property value a specified number of times.
        /// </summary>
        /// <param name="depth">The number of times to repeat the <see cref="IndentString"/> property.</param>
        /// <returns>A string containing the <see cref="IndentString"/> property value a specified number of times.</returns>
        string Indent(int depth)
        {
            StringBuilder sb = new StringBuilder(IndentString.Length * depth);
            for (int i = 0; i < depth; i++)
                sb.Append(IndentString);

            return sb.ToString();
        }

        /// <summary>
        /// Specifies the indentation to use for each level when displaying the object graph.
        /// </summary>
        /// <value>A string value; the default is three blank spaces.</value>
        public string IndentString
        {
            get
            {
                return "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"; //indentString;
            }
        }
    }
}
